以太坊之中 Wallet 可能是兩種,分別是 EOA 以及 Smart Contract Wallet(通常是多簽錢包)。以及最新的抽象帳戶(Account Abstraction),一筆交易在使用者使用錢包簽核(EOA 或多簽都有可能)之後,將其送到 mempool 中,最後礦工會將其打包到區塊中。後面兩個部分我們在之前的 nonce、Tx、PoW 提過了,今天的重點就在錢包這個部分。
以太坊的錢包大致上延續制比特幣錢包的設計,而比特幣錢包的設計機制(稱為 HD Wallet, Hierarchical Deterministic Wallet)主要有三個:BIP32, BIP39, BIP44。
m / purpose' / coin_type' / account' / change / address_index
因為註冊一個新錢包的時候錢包會產出私鑰給每位使用者,網站可以利用這些密私鑰簽核的過程來達到各種需求,而錢包在遇見新的用戶的時候要有一個足夠隨機且安全的生成種子機制,可見 How does MetaMask generate your keys?。
如上所述,隨機演算法會生成隨機種子(BIP-39)也就是註記詞,之後會生成主鑰(Master Key)。主鑰再生成私鑰(BIP-32),私鑰生成公鑰,公鑰生成地址(BIP-44)。
生成完私鑰之後需要把它好好的藏在 Local Storage 之中,基本上是透過加密的方式存在瀏覽器的內存之中(不是本機磁碟),然後解密顯示在小狐狸的 Interface(如果用戶想看見註記詞的話)讓我們導出。
而 Metamask 也宣布要棄用原先使用的兩種私鑰儲存與顯示的 RPC API:eth_decrypt 和 eth_getEncryptionPublicKey。放棄的原因是原先使用的 EIP-1098 會使用用戶的私鑰進行加密,那這把私鑰當然也會用來簽核交易,這樣是有安全上的疑慮的。詳細可見:Dual Use Keys: Why NOT to use the same key for signing and encryption。
那錢包作為私鑰管理器,在我們點選 dapp 時已知 dapp 會要求使用私鑰管理器簽核這筆交易,在小狐狸是如何來簽核我們的交易的呢?
基本上會從瀏覽器的內存中將私鑰解密出來,並且將其導入以下函式中進行簽核,做成數位簽章以後回傳。程式碼取自 MetaMask/eth-sig-util/personal-sign.ts:
/**
* Create an Ethereum-specific signature for a message.
*
* This function is equivalent to the `eth_sign` Ethereum JSON-RPC method as specified in EIP-1417,
* as well as the MetaMask's `personal_sign` method.
*
* @param options - The personal sign options.
* @param options.privateKey - The key to sign with.
* @param options.data - The hex data to sign.
* @returns The '0x'-prefixed hex encoded signature.
*/
export function personalSign({
privateKey,
data,
}: {
privateKey: Buffer;
data: ToBufferInputTypes;
}): string {
if (isNullish(data)) {
throw new Error('Missing data parameter');
} else if (isNullish(privateKey)) {
throw new Error('Missing privateKey parameter');
}
const message = legacyToBuffer(data);
const msgHash = hashPersonalMessage(message);
const sig = ecsign(msgHash, privateKey);
const serialized = concatSig(toBuffer(sig.v), sig.r, sig.s);
return serialized;
}
註解提到的兩個東西:EIP-1417, personalSign
以下是 MetaMask 提供的範例:
當使用者打開了他們的 MetaMask,他們會看見各式各樣的資產,預設之中 MetaMask 會自動偵測知名的代幣並顯示出來。如果要顯示其他大部分的代幣使用者就必須自行將他們加入。
雖然我們可以在 UI 之中設計一個 Add Token button 來, 但這個過程可能是繁瑣且容易出錯的。如果希望增加使用者體驗和安全性,我們可以利用 EIP-747 中的 wallet_watchAsset API。
過往我們在多簽錢包的時候提到過,在合約中可以由多個合約擁有者去決定一個交易或數筆交易是否要被簽核並送出,合約內部也可以撰寫各種為了這個錢包設計的機制。而當今的社群期盼可以讓一個 Account 同時擁有 EOA 與多簽錢包的特性,例如:EIP-3074 & ERC-4337,未來我會在 Medium 撰文特別介紹 Account abstraction,有興趣的大家記得追蹤我呀!
最後歡迎大家拍打餵食大學生
0x2b83c71A59b926137D3E1f37EF20394d0495d72d